//
// Created by buk on 8/28/24.
//
#include <future>
#include <fstream>
#include <regex>
#include <boost/filesystem.hpp>
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/json_parser.hpp>

#define BOOST_SPIRIT_THREADSAFE

#include <boost/signals2.hpp>

#include "spdlog/spdlog.h"
#include "../../../thirdparty/Simple-Web-Server/client_http.hpp"
#include "../../../thirdparty/Simple-Web-Server/server_http.hpp"

#include "web_server.h"
#include "../base/system_config.h"

#include <iostream>
#include <fstream>
#include <string>

using HttpServer = SimpleWeb::Server<SimpleWeb::HTTP>;
using namespace std;

static bool startsWithIgnoreCase(const std::string_view &a,
                                 const std::string_view &b) {
    if (a.size() < b.size()) {
        return false;
    }
    for (size_t i = 0; i < b.size(); i++) {
        if (::tolower(a[i]) != ::tolower(b[i])) {
            return false;
        }
    }
    return true;
}


std::string parseMultipartFormData(const std::string &data, std::string boundary, std::string web_path) {
    std::cout << "entering parseMultipartFormData" << std::endl;
    std::istringstream iss(data);
    std::string line;

    // 读取每个part
    while (std::getline(iss, line)) {
        if (line.find(boundary) != std::string::npos) {
            std::string name, fileName;

            std::getline(iss, line); // 读取Content-Disposition

            if (startsWithIgnoreCase(line, "content-disposition")) {
                static const std::string_view nameKey = "name=";
                static const std::string_view fileNameKey = "filename=";

                std::string valueView = line;

                // Extract name
                auto namePos = line.find(nameKey);
                if (namePos == std::string::npos) {
                    // name absent
                    return NULL;
                }
                namePos += nameKey.size();
                size_t nameEnd;
                if (line[namePos] == '"') {
                    ++namePos;
                    nameEnd = line.find('"', namePos);
                } else {
                    nameEnd = line.find(';', namePos);
                }
                if (nameEnd == std::string::npos) {
                    // name end not found
                    return NULL;
                }
                name =
                        valueView.substr(namePos, nameEnd - namePos);

                // Extract filename
                auto fileNamePos = line.find(fileNameKey, nameEnd);
                if (fileNamePos != std::string::npos) {
                    fileNamePos += fileNameKey.size();
                    size_t fileNameEnd;
                    if (valueView[fileNamePos] == '"') {
                        ++fileNamePos;
                        fileNameEnd = valueView.find('"', fileNamePos);
                    } else {
                        fileNameEnd = valueView.find(';', fileNamePos);
                    }
                    fileName =
                            valueView.substr(fileNamePos,
                                             fileNameEnd - fileNamePos);

                }
            }

            // std::string name = line.substr(line.find("name=\"") + 6);
            // name = name.substr(0, name.find("\""));
            spdlog::debug(line);

            // Content-Type: 行
            std::getline(iss, line);
            if (line.find("Content-Type") != std::string::npos) {
                spdlog::debug(line);
                // 跳过空行
                std::getline(iss, line);
            } else {
                // 跳过空行
            }
            // 读取数据
            std::string content;
            while (std::getline(iss, line)) {
                if (line.find(boundary) != std::string::npos) {
                    break; // 到达下一个part
                }
                content += line + "\n";
            }
            // 输出数据
            std::cout << "Name: " << name << std::endl;
            std::cout << "FileName: " << fileName << std::endl;
            // 如果是图片，则把content内容保存为图片文件
            // std::cout << "Content: " << content << std::endl;

            // std::string content = "这是要保存的文本内容";
            std::ofstream file(web_path + "/" + fileName); // 创建ofstream对象，打开文件

            if (file.is_open()) { // 检查文件是否成功打开
                file << content; // 将字符串写入文件
                file.close(); // 关闭文件
                std::cout << "文件保存成功" << std::endl;
            } else {
                std::cout << "文件保存失败" << std::endl;
            }

            return fileName;
        }
    }
}


namespace ai::facial_recognition {

    WebServer::WebServer() {
        this->server_port = 8080;
        this->init();
    }

    WebServer::WebServer(int port) {
        this->server_port = port;
        this->init();
    }

    WebServer::~WebServer() {
    }


    void WebServer::subscribeRequest(const RequestSignalType::slot_type &request_slot) {
        request_signal_.connect(request_slot);
    }

    void WebServer::start() {
        // 启动服务器，接收服务器实际监听的端口
        std::promise<unsigned short> server_port;
        this->jthread_server_ = std::jthread([this, &server_port]() {
            // Start server
            this->server.start([&server_port](unsigned short port) {
                server_port.set_value(port);
            });
        });

        spdlog::info("Web server is listening on port " + std::to_string(server_port.get_future().get()));
    }

    void WebServer::stop() {
        this->server.stop();
        this->jthread_server_.request_stop();
        spdlog::info("Web server stopped");
    }

    void WebServer::init() {
        this->server.config.port = this->server_port;

        SystemConfig system_config;
        this->web_root = system_config.readKey<std::string>("global", "web_root", "wwwroot");

        // 检查人脸特征值
        server.resource["^/detect_face$"]["POST"] = [this](shared_ptr <HttpServer::Response> response,
                                                           shared_ptr <HttpServer::Request> request) {
            thread work_thread([this, response, request] {
                auto msg = std::make_shared<RequestMessage>();
                std::string fileName;
                std::string content_type_;
                std::string boundary_, dashBoundaryCrlf_, crlfDashBoundary_;
                static const std::string_view multipart = "multipart/form-data";
                static const std::string_view boundaryEq = "boundary=";
                const std::string dash_ = "--";
                const std::string crlf_ = "\r\n";

                for (auto it = request->header.begin(); it != request->header.end(); ++it) {
                    std::string temp = it->first;
                    spdlog::debug(temp + " : " + it->second);
                    if (temp == "Content-Type") {
                        auto contentType = it->second;
                        if (!startsWithIgnoreCase(contentType, multipart)) {
                            return;
                        }
                        auto pos = contentType.find(boundaryEq, multipart.size());
                        if (pos == std::string::npos) {
                            return;
                        }

                        pos += boundaryEq.size();
                        size_t pos2;
                        if (contentType[pos] == '"') {
                            ++pos;
                            pos2 = contentType.find('"', pos);
                        } else {
                            pos2 = contentType.find(';', pos);
                        }
                        if (pos2 == std::string::npos)
                            pos2 = contentType.size();

                        boundary_ = contentType.substr(pos, pos2 - pos);

                        fileName = parseMultipartFormData(request->content.string(), boundary_, this->web_root);
                    }
                }

                spdlog::debug(request->content.size());
                spdlog::debug("filename: " + fileName);
                msg->image_file_name = fileName;
                msg->web_root = this->web_root;

                // 上传图片成功，开始自动识别
                this->request_signal_(msg);

                writeResponse(response, msg);
            });

            work_thread.detach();
        };
        // 保存待比较的成员特征值等信息
        server.resource["^/member_info$"]["POST"] = [this](std::shared_ptr <HttpServer::Response> response,
                                                          std::shared_ptr <HttpServer::Request> request) {
            try {
                boost::property_tree::ptree pt;
                read_json(request->content, pt);

                auto save_result = _data_store->save_member_info(pt.get<int>("member_id"),
                                              pt.get<string>("member_name"),
                                              pt.get<string>("face_feature"),
                                              pt.get<int>("face_feature_size"));
                auto msg = std::make_shared<BaseMessage>();
                if (save_result == 0) {
                    //保存失败
                    msg->context.put<int>("errcode", 10000);
                    msg->context.put<string>("errmsg", "failed");
                }
                this->writeResponse(response, msg);
            }
            catch (const exception &e) {
                *response << "HTTP/1.1 400 Bad Request\r\nContent-Length: " << strlen(e.what()) << "\r\n\r\n"
                          << e.what();
            }
        };

        // 删除成员信息
        server.resource["^/member_info/([0-9]+)/delete$"]["POST"] = [this](std::shared_ptr <HttpServer::Response> response,
                                                             std::shared_ptr <HttpServer::Request> request) {
            auto msg = std::make_shared<BaseMessage>();

            try {
                std::string  str_member_id = request->path_match[1].str();
                auto member_id = atoi(str_member_id.c_str());
                auto delete_result = _data_store->delete_member_info(member_id);
                if (delete_result == 0) {
                    //删除失败
                    msg->context.put<int>("errcode", 10000);
                    msg->context.put<string>("errmsg", "It failed to delete member info.");
                }
            }
            catch (const exception &e) {
                *response << "HTTP/1.1 400 Bad Request\r\nContent-Length: " << strlen(e.what()) << "\r\n\r\n"
                          << e.what();
            }

            this->writeResponse(response, msg);
        };


        // 保存设备信息
        server.resource["^/device_info"]["POST"] = [this](std::shared_ptr <HttpServer::Response> response,
                                                          std::shared_ptr <HttpServer::Request> request) {
            try {
                boost::property_tree::ptree pt;
                read_json(request->content, pt);

                auto save_result = _data_store->save_device_info(pt.get<int>("device_id"),
                                              pt.get<string>("device_name"),
                                              pt.get<int>("device_type"),
                                              pt.get<string>("rtsp_url"));
                auto msg = std::make_shared<BaseMessage>();
                if (save_result == 0) {
                    //保存失败
                    msg->context.put<int>("errcode", 10000);
                    msg->context.put<string>("errmsg", "failed");
                }
                this->writeResponse(response, msg);
            }
            catch (const exception &e) {
                *response << "HTTP/1.1 400 Bad Request\r\nContent-Length: " << strlen(e.what()) << "\r\n\r\n"
                          << e.what();
            }
        };

        // Add resources using path-regex and method-string, and an anonymous function
        // POST-example for the path /string, responds the posted string
        server.resource["^/string$"]["POST"] = [this](shared_ptr <HttpServer::Response> response,
                                                      shared_ptr <HttpServer::Request> request) {
            auto content = request->content.string();
            auto msg = std::make_shared<BaseMessage>();
            msg->context.put("content", content);
            this->writeResponse(response, msg);
        };

        server.resource["^/json$"]["POST"] = [this](std::shared_ptr <HttpServer::Response> response,
                                                    std::shared_ptr <HttpServer::Request> request) {
            try {
                boost::property_tree::ptree pt;
                read_json(request->content, pt);

                auto msg = std::make_shared<BaseMessage>();
                msg->context.put("firstName", pt.get<string>("firstName"));
                msg->context.put("lastName", pt.get<string>("lastName"));
                this->writeResponse(response, msg);
            }
            catch (const exception &e) {
                *response << "HTTP/1.1 400 Bad Request\r\nContent-Length: " << strlen(e.what()) << "\r\n\r\n"
                          << e.what();
            }
        };

        // GET-example for the path /info
        // Responds with request-information
        server.resource["^/info$"]["GET"] = [](std::shared_ptr <HttpServer::Response> response,
                                               std::shared_ptr <HttpServer::Request> request) {
            stringstream stream;
            stream << "<h1>Request from " << request->remote_endpoint().address().to_string() << ":"
                   << request->remote_endpoint().port() << "</h1>";

            stream << request->method << " " << request->path << " HTTP/" << request->http_version;

            stream << "<h2>Query Fields</h2>";
            auto query_fields = request->parse_query_string();
            for (auto &field: query_fields)
                stream << field.first << ": " << field.second << "<br>";

            stream << "<h2>Header Fields</h2>";
            for (auto &field: request->header)
                stream << field.first << ": " << field.second << "<br>";

            response->write(stream);
        };

        // GET-example for the path /match/[number], responds with the matched string in path (number)
        // For instance a request GET /match/123 will receive: 123
        server.resource["^/match/([0-9]+)$"]["GET"] = [this](std::shared_ptr <HttpServer::Response> response,
                                                             std::shared_ptr <HttpServer::Request> request) {
            auto msg = std::make_shared<BaseMessage>();

            // Set the response content
            msg->context.put("content", request->path_match[1].str());
            this->writeResponse(response, msg);
        };

        // GET-example simulating heavy work in a separate thread
        server.resource["^/work$"]["GET"] = [](std::shared_ptr <HttpServer::Response> response,
                                               std::shared_ptr <HttpServer::Request> /*request*/) {
            thread work_thread([response] {
                this_thread::sleep_for(chrono::seconds(5));
                response->write("Work done");
            });
            work_thread.detach();
        };

        // Default GET-example. If no other matches, this anonymous function will be called.
        // Will respond with content in the web/-directory, and its subdirectories.
        // Default file: index.html
        // Can for instance be used to retrieve an HTML 5 client that uses REST-resources on this server
        server.default_resource["GET"] = [this](std::shared_ptr <HttpServer::Response> response,
                                                std::shared_ptr <HttpServer::Request> request) {
            try {
                auto web_root_path = boost::filesystem::canonical(this->web_root);
                std::cout << web_root_path << std::endl;
                auto path = boost::filesystem::canonical(web_root_path / request->path);
                std::cout << request->path << std::endl;
                // Check if path is within web_root_path
                if (distance(web_root_path.begin(), web_root_path.end()) > distance(path.begin(), path.end()) ||
                    !equal(web_root_path.begin(), web_root_path.end(), path.begin()))
                    throw invalid_argument("path must be within root path");
                if (boost::filesystem::is_directory(path))
                    path /= "index.html";

                SimpleWeb::CaseInsensitiveMultimap header;

                // Uncomment the following line to enable Cache-Control
                // header.emplace("Cache-Control", "max-age=86400");

                auto ifs = make_shared<ifstream>();
                ifs->open(path.string(), ifstream::in | ios::binary | ios::ate);

                if (*ifs) {
                    auto length = ifs->tellg();
                    ifs->seekg(0, ios::beg);

                    header.emplace("Content-Length", to_string(length));
                    response->write(header);

                    // Trick to define a recursive function within this scope (for example purposes)
                    class FileServer {
                    public:
                        static void read_and_send(const shared_ptr <HttpServer::Response> &response,
                                                  const shared_ptr <ifstream> &ifs) {
                            // Read and send 128 KB at a time
                            static vector<char> buffer(131072); // Safe when server is running on one thread
                            streamsize read_length;
                            if ((read_length = ifs->read(&buffer[0], static_cast<streamsize>(buffer.size())).gcount()) >
                                0) {
                                response->write(&buffer[0], read_length);
                                if (read_length == static_cast<streamsize>(buffer.size())) {
                                    response->send([response, ifs](const SimpleWeb::error_code &ec) {
                                        if (!ec)
                                            read_and_send(response, ifs);
                                        else
                                            cerr << "Connection interrupted" << endl;
                                    });
                                }
                            }
                        }
                    };
                    FileServer::read_and_send(response, ifs);
                } else
                    throw invalid_argument("could not read file");
            }
            catch (const exception &e) {
                response->write(SimpleWeb::StatusCode::client_error_bad_request,
                                "Could not open path " + request->path + ": " + e.what());
            }
        };

        server.on_error = [](shared_ptr <HttpServer::Request> /*request*/, const SimpleWeb::error_code & /*ec*/) {
            // Handle errors here
            // Note that connection timeouts will also call this handle with ec set to SimpleWeb::errc::operation_canceled
        };
    }

    void WebServer::writeResponse(std::shared_ptr <HttpServer::Response> response, std::shared_ptr <BaseMessage> msg) {
        auto node1 = msg->context.get_optional<int>("errcode");
        if (!node1.is_initialized()) {
            msg->context.put<int>("errcode", 0);
        }

        auto node2 = msg->context.get_optional<int>("errmsg");
        if (!node2.is_initialized()) {
            msg->context.put("errmsg", "");
        }

        std::ostringstream os;
        write_json(os, msg->context);

        response->write(os.str());
    }

    void WebServer::set_data_store(DataStore *data_store) {
        _data_store = data_store;
    }

}